#include "RayTracingComputeProcess.h"
#include "Utils/LogUtil.h"
#include "Render/RenderState.h"
#include "Core/CoreGlobals.h"
#include "Game/Scene/Entity.h"
#include "Game/Component/Model.h"
#include "Render/Buffer/VertexBuffer.h"
#include "Render/Buffer/IndexBuffer.h"
#include "Render/Buffer/CustomBuffer.h"
#include "Render/Texture/CustomTexture.h"
#include "LocalShader.h"
#include "Render/VulkanTool.h"

void RayTracingComputeProcess::OnInit()
{
	mMaxFrame = 100000;
	mFrame = 0;
	mLastView = mGameScene->GetCamera().GetView();
	mLastProj = mGameScene->GetCamera().GetProj();

	fCompareX = [this](const TriangleGpu& t1, const TriangleGpu& t2)
	{
		return mAllVertex[t1.index[0]].position.x + mAllVertex[t1.index[1]].position.x + mAllVertex[t1.index[2]].position.x <
			mAllVertex[t2.index[0]].position.x + mAllVertex[t2.index[1]].position.x + mAllVertex[t2.index[2]].position.x;

		return true;
	};

	fCompareY = [this](const TriangleGpu& t1, const TriangleGpu& t2)
	{
		return mAllVertex[t1.index[0]].position.y + mAllVertex[t1.index[1]].position.y + mAllVertex[t1.index[2]].position.y <
			mAllVertex[t2.index[0]].position.y + mAllVertex[t2.index[1]].position.y + mAllVertex[t2.index[2]].position.y;

		return true;
	};

	fCompareZ = [this](const TriangleGpu& t1, const TriangleGpu& t2)
	{
		return mAllVertex[t1.index[0]].position.z + mAllVertex[t1.index[1]].position.z + mAllVertex[t1.index[2]].position.z <
			mAllVertex[t2.index[0]].position.z + mAllVertex[t2.index[1]].position.z + mAllVertex[t2.index[2]].position.z;

		return true;
	};

	mLocalEntitys = mGameScene->GetEntitysByType<LocalEntity, Model>();

	uint32 vertexOffset = 0;

	for (uint32 i = 0; i < mLocalEntitys.size(); ++i)
	{
		glm::mat4 modelMatrix = mLocalEntitys[i]->GetTransform().GetWorldMatrix();

		Model& model = mLocalEntitys[i]->GetComponent<Model>();

		mAllMaterial.push_back(mLocalEntitys[i]->mMaterial);

		for (uint32 k = 0; k < model.mVertexs.size(); ++k)
		{
			VertexGpu vertex;
			vertex.position = glm::vec3(modelMatrix * glm::vec4(model.mVertexs[k].Position, 1.f));
			vertex.normal = glm::mat3(modelMatrix) * model.mVertexs[k].Normal;
			vertex.tangent = glm::mat3(modelMatrix) * model.mVertexs[k].Tangent;
			vertex.bitangent = glm::mat3(modelMatrix) * model.mVertexs[k].Bitangent;
			vertex.uv = model.mVertexs[k].TexC;
			mAllVertex.push_back(vertex);
		}

		for (uint32 k = 0; k < model.mIndices.size(); k += 3)
		{
			mAllTriangle.push_back(TriangleGpu(glm::uvec3(model.mIndices[k] + vertexOffset, model.mIndices[k + 1] + vertexOffset, model.mIndices[k + 2] + vertexOffset), mAllMaterial.size() - 1));
		}

		vertexOffset += model.mVertexs.size();
	}

	// mAllBVHNode.push_back(BVHNodeGpu());
	BuildBVHTree(0, mAllTriangle.size() - 1, 20);

	Ubo ubo;
	ubo.projview = mLastProj * mLastView;
	ubo.viewInverse = glm::inverse(mLastView);
	ubo.projInverse = glm::inverse(mLastProj);
	ubo.frame = mFrame;

	ENQUEUE_RENDER_COMMAND(BuildModel)([this, ubo](VkCommandBuffer commandBuffer)
		{
			Ubo uboData = ubo;

			mUboBuffer = CustomBuffer::Builder()
				.SetData(&uboData)
				.SetSize(sizeof(Ubo))
				.SetMemoryProperty(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
				.Build();

			mMaterialBuffer = CustomBuffer::Builder()
				.SetData(mAllMaterial.data())
				.SetSize(sizeof(MaterialGpu) * mAllMaterial.size())
				.SetUsage(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)
				.Build();

			mVertexBuffer = CustomBuffer::Builder()
				.SetData(mAllVertex.data())
				.SetSize(sizeof(VertexGpu) * mAllVertex.size())
				.SetUsage(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)
				.Build();

			mTriangleBuffer = CustomBuffer::Builder()
				.SetData(mAllTriangle.data())
				.SetSize(sizeof(TriangleGpu) * mAllTriangle.size())
				.SetUsage(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)
				.Build();

			mBVHNodeBuffer = CustomBuffer::Builder()
				.SetData(mAllBVHNode.data())
				.SetSize(sizeof(BVHNodeGpu) * mAllBVHNode.size())
				.SetUsage(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)
				.Build();

			mCubemapTexture = CubemapTexture::Builder()
				.SetAutoMip()
				.SetPaths({
				"Hdr\\TreeSky_nz.hdr",
				"Hdr\\TreeSky_pz.hdr",
				"Hdr\\TreeSky_py.hdr",
				"Hdr\\TreeSky_ny.hdr",
				"Hdr\\TreeSky_px.hdr",
				"Hdr\\TreeSky_nx.hdr"
					})
				.SetFormat(VK_FORMAT_R32G32B32A32_SFLOAT)
				.Build();

			mCubemapSampler = RHISampler::Builder()
				.SetAddressModeU(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE)
				.SetAddressModeV(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE)
				.SetAddressModeW(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE)
				.SetCompareOp(VK_COMPARE_OP_NEVER)
				.SetMaxLod(mCubemapTexture->GetMipLevels())
				.SetBorderColor(VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE)
				.Build();

			// Check if requested image format supports image storage operations
			VkFormatProperties formatProperties;
			vkGetPhysicalDeviceFormatProperties(GVulkanRHI->GetPhysicalDevice(), VK_FORMAT_R8G8B8A8_UNORM, &formatProperties);
			assert(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT);

			mResultTexture = CustomTexture::Builder()
				.SetUsage(VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT)
				.Build();

			mColorTexture = CustomTexture::Builder()
				.SetUsage(VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT)
				.SetMultiple(true)
				.Build(this, "ColorRenderTarget");


			VkCommandBuffer onceCommandBuffer = GRenderState->BeginSingleCommand();

			TextureTransfer()
				.SetSrcStageMask(VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR)
				.SetSrcAccessMask(VK_ACCESS_MEMORY_READ_BIT)
				.SetDstStageMask(VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR)
				.SetDstAccessMask(VK_ACCESS_MEMORY_WRITE_BIT)
				.SetNewLayout(VK_IMAGE_LAYOUT_GENERAL)
				.SetTexture(mResultTexture)
				.Transfer(onceCommandBuffer);

			TextureTransfer()
				.SetSrcStageMask(VK_PIPELINE_STAGE_TRANSFER_BIT)
				.SetSrcAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT)
				.SetDstStageMask(VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT)
				.SetDstAccessMask(VK_ACCESS_SHADER_READ_BIT)
				.SetNewLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
				.SetTexture(mColorTexture)
				.Transfer(onceCommandBuffer, true);

			GRenderState->FlushSingleCommand(onceCommandBuffer);


			mDescriptor = RHIDescriptor::Builder()
				.PushShader<RayTracingComputeShader>()
				.SetDescriptorCount(0, 1)
				.Build();


			mDescriptor->SetValue("ubo", mUboBuffer);
			mDescriptor->SetValue("resultImage", mResultTexture, VK_IMAGE_LAYOUT_GENERAL);
			mDescriptor->SetValue("materials", mMaterialBuffer);
			mDescriptor->SetValue("vertexs", mVertexBuffer);
			mDescriptor->SetValue("triangles", mTriangleBuffer);
			mDescriptor->SetValue("bvhNodes", mBVHNodeBuffer);
			mDescriptor->SetValue("samplerCubeMap", mCubemapTexture, mCubemapSampler);
			mDescriptorMask = mDescriptor->AllocateDescriptor();


			mPipeline = ComputePipeline::Builder()
				.SetDescriptor(mDescriptor)
				.Build();

			mCommandBuffer = RHICommandBuffer::Builder()
				.Build();

			mWaitGraphicsSemaphore = RHISemaphore::Builder()
				.SetWaitDstStage(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT)
				.SetCount(1)
				.SetSignaled(true)
				.Build();

			mSignalComputeSemaphore = RHISemaphore::Builder()
				.SetWaitDstStage(VK_PIPELINE_STAGE_VERTEX_INPUT_BIT)
				.SetCount(1)
				.Build();
		});
}

void RayTracingComputeProcess::OnTick(float deltaTime)
{
	glm::mat4 currView = mGameScene->GetCamera().GetView();
	glm::mat4 currProj = mGameScene->GetCamera().GetProj();

	if (memcmp(&mLastView, &currView, sizeof(glm::mat4)) == 0 && memcmp(&mLastProj, &currProj, sizeof(glm::mat4)) == 0)
	{
		// No change, frame ++ if less than maxframe
		if (mFrame < mMaxFrame)
		{
			mFrame++;
		}
		else
		{
			// Do not render ray until matrix change
			return;
		}
	}
	else
	{
		// Reset frame and matrix
		mFrame = 0;
		mLastView = currView;
		mLastProj = currProj;
	}


	Ubo ubo;
	ubo.projview = mLastProj * mLastView;
	ubo.viewInverse = glm::inverse(mLastView);
	ubo.projInverse = glm::inverse(mLastProj);
	ubo.frame = mFrame;

	ENQUEUE_RENDER_COMMAND(RecordCommand)([this, ubo](VkCommandBuffer commandBuffer)
		{
			Ubo uboData = ubo;

			mUboBuffer->WriteMemory(&uboData);

			// GRenderState->AddWaitSemaphore(mSignalComputeSemaphore);
			// GRenderState->AddSignalSemaphore(mWaitGraphicsSemaphore);

			VkCommandBuffer computeCommandBuffer = mCommandBuffer->BeginCommandBuffer();

			vkCmdBindPipeline(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, mPipeline->GetPipeline());
			vkCmdBindDescriptorSets(computeCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, mPipeline->GetPipelineLayout(), 0, 1, &mDescriptorMask.GetDescriptorSet(), 0, 0);

			vkCmdDispatch(computeCommandBuffer, GRenderState->GetWidth() / 16, GRenderState->GetHeight() / 16, 1);

			TextureTransfer()
				.SetSrcStageMask(VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR)
				.SetSrcAccessMask(VK_ACCESS_MEMORY_WRITE_BIT)
				.SetDstStageMask(VK_PIPELINE_STAGE_TRANSFER_BIT)
				.SetDstAccessMask(VK_ACCESS_TRANSFER_READ_BIT)
				.SetNewLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
				.SetTexture(mResultTexture)
				.Transfer(computeCommandBuffer);

			TextureTransfer()
				.SetSrcStageMask(VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT)
				.SetSrcAccessMask(VK_ACCESS_SHADER_READ_BIT)
				.SetDstStageMask(VK_PIPELINE_STAGE_TRANSFER_BIT)
				.SetDstAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT)
				.SetNewLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
				.SetTexture(mColorTexture)
				.Transfer(computeCommandBuffer);

			VkImageCopy copyRegion{};
			copyRegion.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
			copyRegion.srcOffset = { 0, 0, 0 };
			copyRegion.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
			copyRegion.dstOffset = { 0, 0, 0 };
			copyRegion.extent = { GRenderState->GetWidth(), GRenderState->GetHeight(), 1 };
			vkCmdCopyImage(
				commandBuffer,
				mResultTexture->GetImage(),
				VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
				mColorTexture->GetImage(),
				VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
				1, &copyRegion);

			TextureTransfer()
				.SetSrcStageMask(VK_PIPELINE_STAGE_TRANSFER_BIT)
				.SetSrcAccessMask(VK_ACCESS_TRANSFER_READ_BIT)
				.SetDstStageMask(VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR)
				.SetDstAccessMask(VK_ACCESS_MEMORY_WRITE_BIT)
				.SetNewLayout(VK_IMAGE_LAYOUT_GENERAL)
				.SetTexture(mResultTexture)
				.Transfer(commandBuffer);

			TextureTransfer()
				.SetSrcStageMask(VK_PIPELINE_STAGE_TRANSFER_BIT)
				.SetSrcAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT)
				.SetDstStageMask(VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT)
				.SetDstAccessMask(VK_ACCESS_SHADER_READ_BIT)
				.SetNewLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
				.SetTexture(mColorTexture)
				.Transfer(commandBuffer);

			mCommandBuffer->FlushCommandBuffer(computeCommandBuffer);

			// mCommandBuffer->FlushCommandBuffer(computeCommandBuffer, { mWaitGraphicsSemaphore }, { mSignalComputeSemaphore });

		});
}

void RayTracingComputeProcess::OnResize()
{
	mResultTexture = CustomTexture::Builder()
		.SetUsage(VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT)
		.Build();

	mColorTexture = CustomTexture::Builder()
		.SetUsage(VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT)
		.SetMultiple(true)
		.Build(this, "ColorRenderTarget");

	VkCommandBuffer commandBuffer = GRenderState->BeginSingleCommand();

	TextureTransfer()
		.SetSrcStageMask(VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR)
		.SetSrcAccessMask(VK_ACCESS_MEMORY_READ_BIT)
		.SetDstStageMask(VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR)
		.SetDstAccessMask(VK_ACCESS_MEMORY_WRITE_BIT)
		.SetNewLayout(VK_IMAGE_LAYOUT_GENERAL)
		.SetTexture(mResultTexture)
		.Transfer(commandBuffer);

	TextureTransfer()
		.SetSrcStageMask(VK_PIPELINE_STAGE_TRANSFER_BIT)
		.SetSrcAccessMask(VK_ACCESS_TRANSFER_WRITE_BIT)
		.SetDstStageMask(VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT)
		.SetDstAccessMask(VK_ACCESS_SHADER_READ_BIT)
		.SetNewLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
		.SetTexture(mColorTexture)
		.Transfer(commandBuffer, true);

	GRenderState->FlushSingleCommand(commandBuffer);

	mDescriptor->SetValue("resultImage", mResultTexture, VK_IMAGE_LAYOUT_GENERAL);
	mDescriptor->UpdateDescriptor(mDescriptorMask);
}

uint32 RayTracingComputeProcess::BuildBVHTree(uint32 l, uint32 r, uint32 n)
{
	if (l > r)
		return 0;

	// Do not use & to keep node, the address will change
	mAllBVHNode.push_back(BVHNodeGpu());
	const uint32 nodeIndex = static_cast<uint32>(mAllBVHNode.size() - 1);

	for (uint32 i = l; i <= r; ++i)
	{
		const glm::uvec3& idx = mAllTriangle[i].index;

		const glm::vec3& p1 = mAllVertex[idx[0]].position;
		const glm::vec3& p2 = mAllVertex[idx[1]].position;
		const glm::vec3& p3 = mAllVertex[idx[2]].position;

		float minx = Math::Min(p1.x, Math::Min(p2.x, p3.x));
		float miny = Math::Min(p1.y, Math::Min(p2.y, p3.y));
		float minz = Math::Min(p1.z, Math::Min(p2.z, p3.z));
		mAllBVHNode[nodeIndex].aa.x = Math::Min(mAllBVHNode[nodeIndex].aa.x, minx);
		mAllBVHNode[nodeIndex].aa.y = Math::Min(mAllBVHNode[nodeIndex].aa.y, miny);
		mAllBVHNode[nodeIndex].aa.z = Math::Min(mAllBVHNode[nodeIndex].aa.z, minz);

		float maxx = Math::Max(p1.x, Math::Max(p2.x, p3.x));
		float maxy = Math::Max(p1.y, Math::Max(p2.y, p3.y));
		float maxz = Math::Max(p1.z, Math::Max(p2.z, p3.z));
		mAllBVHNode[nodeIndex].bb.x = Math::Max(mAllBVHNode[nodeIndex].bb.x, maxx);
		mAllBVHNode[nodeIndex].bb.y = Math::Max(mAllBVHNode[nodeIndex].bb.y, maxy);
		mAllBVHNode[nodeIndex].bb.z = Math::Max(mAllBVHNode[nodeIndex].bb.z, maxz);
	}

	if ((r - l + 1) <= n)
	{
		mAllBVHNode[nodeIndex].n = r - l + 1;
		mAllBVHNode[nodeIndex].l = l;
		return nodeIndex;
	}

#if 1

	float Cost = FLT_MAX;
	int32 Axis = 0;
	int32 Split = (l + r) / 2;
	for (int32 axis = 0; axis < 3; axis++) {

		if (axis == 0) std::sort(&mAllTriangle[0] + l, &mAllTriangle[0] + r + 1, fCompareX);
		if (axis == 1) std::sort(&mAllTriangle[0] + l, &mAllTriangle[0] + r + 1, fCompareY);
		if (axis == 2) std::sort(&mAllTriangle[0] + l, &mAllTriangle[0] + r + 1, fCompareZ);


		std::vector<glm::vec3> leftMax(r - l + 1, glm::vec3(FLT_MIN, FLT_MIN, FLT_MIN));
		std::vector<glm::vec3> leftMin(r - l + 1, glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX));

		for (uint32 i = l; i <= r; ++i) {

			const glm::uvec3& idx = mAllTriangle[i].index;

			const glm::vec3& p1 = mAllVertex[idx[0]].position;
			const glm::vec3& p2 = mAllVertex[idx[1]].position;
			const glm::vec3& p3 = mAllVertex[idx[2]].position;

			int32 bias = (i == l) ? 0 : 1;

			leftMax[i - l].x = Math::Max(leftMax[i - l - bias].x, Math::Max(p1.x, Math::Max(p2.x, p3.x)));
			leftMax[i - l].y = Math::Max(leftMax[i - l - bias].y, Math::Max(p1.y, Math::Max(p2.y, p3.y)));
			leftMax[i - l].z = Math::Max(leftMax[i - l - bias].z, Math::Max(p1.z, Math::Max(p2.z, p3.z)));

			leftMin[i - l].x = Math::Min(leftMin[i - l - bias].x, Math::Min(p1.x, Math::Min(p2.x, p3.x)));
			leftMin[i - l].y = Math::Min(leftMin[i - l - bias].y, Math::Min(p1.y, Math::Min(p2.y, p3.y)));
			leftMin[i - l].z = Math::Min(leftMin[i - l - bias].z, Math::Min(p1.z, Math::Min(p2.z, p3.z)));
		}


		std::vector<glm::vec3> rightMax(r - l + 1, glm::vec3(FLT_MIN, FLT_MIN, FLT_MIN));
		std::vector<glm::vec3> rightMin(r - l + 1, glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX));

		for (uint32 i = r; i >= l; --i) {

			const glm::uvec3& idx = mAllTriangle[i].index;

			const glm::vec3& p1 = mAllVertex[idx[0]].position;
			const glm::vec3& p2 = mAllVertex[idx[1]].position;
			const glm::vec3& p3 = mAllVertex[idx[2]].position;

			int32 bias = (i == r) ? 0 : 1;

			rightMax[i - l].x = Math::Max(rightMax[i - l + bias].x, Math::Max(p1.x, Math::Max(p2.x, p3.x)));
			rightMax[i - l].y = Math::Max(rightMax[i - l + bias].y, Math::Max(p1.y, Math::Max(p2.y, p3.y)));
			rightMax[i - l].z = Math::Max(rightMax[i - l + bias].z, Math::Max(p1.z, Math::Max(p2.z, p3.z)));

			rightMin[i - l].x = Math::Min(rightMin[i - l + bias].x, Math::Min(p1.x, Math::Min(p2.x, p3.x)));
			rightMin[i - l].y = Math::Min(rightMin[i - l + bias].y, Math::Min(p1.y, Math::Min(p2.y, p3.y)));
			rightMin[i - l].z = Math::Min(rightMin[i - l + bias].z, Math::Min(p1.z, Math::Min(p2.z, p3.z)));

			if (i == 0u)
				break;
		}


		float cost = FLT_MAX;
		int32 split = l;
		for (uint32 i = l; i <= r - 1; i++) {
			float lenx, leny, lenz;

			glm::vec3 leftAA = leftMin[i - l];
			glm::vec3 leftBB = leftMax[i - l];
			lenx = leftBB.x - leftAA.x;
			leny = leftBB.y - leftAA.y;
			lenz = leftBB.z - leftAA.z;
			float leftS = 2.0 * ((lenx * leny) + (lenx * lenz) + (leny * lenz));
			float leftCost = leftS * (i - l + 1);

			glm::vec3 rightAA = rightMin[i + 1 - l];
			glm::vec3 rightBB = rightMax[i + 1 - l];
			lenx = rightBB.x - rightAA.x;
			leny = rightBB.y - rightAA.y;
			lenz = rightBB.z - rightAA.z;
			float rightS = 2.0 * ((lenx * leny) + (lenx * lenz) + (leny * lenz));
			float rightCost = rightS * (r - i);


			float totalCost = leftCost + rightCost;
			if (totalCost < cost) {
				cost = totalCost;
				split = i;
			}
		}

		if (cost < Cost) {
			Cost = cost;
			Axis = axis;
			Split = split;
		}
	}

	if (Axis == 0) std::sort(&mAllTriangle[0] + l, &mAllTriangle[0] + r + 1, fCompareX);
	if (Axis == 1) std::sort(&mAllTriangle[0] + l, &mAllTriangle[0] + r + 1, fCompareY);
	if (Axis == 2) std::sort(&mAllTriangle[0] + l, &mAllTriangle[0] + r + 1, fCompareZ);

	const uint32 left = BuildBVHTree(l, Split, n);
	const uint32 right = BuildBVHTree(Split + 1, r, n);

#else

	float lenx = mAllBVHNode[nodeIndex].bb.x - mAllBVHNode[nodeIndex].aa.x;
	float leny = mAllBVHNode[nodeIndex].bb.y - mAllBVHNode[nodeIndex].aa.y;
	float lenz = mAllBVHNode[nodeIndex].bb.z - mAllBVHNode[nodeIndex].aa.z;

	if (lenx >= leny && lenx >= lenz)
		std::sort(&mAllTriangle[0] + l, &mAllTriangle[0] + r + 1, fCompareX);

	if (leny >= lenx && leny >= lenz)
		std::sort(&mAllTriangle[0] + l, &mAllTriangle[0] + r + 1, fCompareY);

	if (lenz >= lenx && lenz >= leny)
		std::sort(&mAllTriangle[0] + l, &mAllTriangle[0] + r + 1, fCompareZ);

	int mid = (l + r) / 2;
	const uint32 left = BuildBVHTree(l, mid, n);
	const uint32 right = BuildBVHTree(mid + 1, r, n);

#endif

	mAllBVHNode[nodeIndex].left = left;
	mAllBVHNode[nodeIndex].right = right;

	return nodeIndex;
}

